﻿#light

open System
open System.Collections.Generic
open NUnit.Framework
open PDA

[<TestFixture;
    Description("Test suite for F# PDA implementation")>]
type Test() = 
    
    [<Test;
        Category("Normal Case");
        Description("Tests if the two Configs are equal")>]
    member x.TestConfigEqual() =
        let c = {state=PDA.State.q0; input=['a';]; stack=['Z';];}
        let c' = {state=PDA.State.q0; input=['a';]; stack=['a';'Z';];}
        Assert.AreEqual(c, {state=PDA.State.q0; input=['a';]; stack=['Z';];})
        Assert.AreEqual(c', {state=PDA.State.q0; input=['a';]; stack=['a';'Z';];})
        Assert.AreNotEqual(c, {state=PDA.State.q0; input=['a';]; stack=['a';'Z';];})
        
        

    [<Test;
        Category("Normal Case");
        Description("Should return the minimum of either [], or tail")>]
    member x.TestCombinatorialSubtraction() =
        Assert.IsTrue(combSub [1;] = List.empty)
        Assert.IsTrue(combSub [] = List.empty)
        Assert.AreEqual([2;3;4;], combSub [1;2;3;4;])
        
        
    [<Test;
        Category("Normal Case");
        Description("Should return the head of a list or []")>]
    member x.TestHead() =
        Assert.IsTrue(head [1;] = [1;])
        Assert.IsTrue(head [] = List.empty)
        Assert.AreEqual([1;], head [1;2;3;4;])
        
        
    [<Test;
        Category("Normal Case");
        Description("Should return a new Configuration with only the front element of the input and stack")>]
    member x.TestNormalizeConfig() =
        Assert.AreEqual({state=PDA.State.q0; input=['a';]; stack=['Z';];}, 
                        normalizeConfig {state=PDA.State.q0; input=['a';'a';'b';]; stack=['Z';];})
        Assert.AreEqual({state=PDA.State.q0; input=['a';]; stack=['Z';];}, 
                        normalizeConfig {state=PDA.State.q0; input=['a';]; stack=['Z';];})
        Assert.AreEqual({state=PDA.State.q0; input=[]; stack=['Z';];}, 
                        normalizeConfig {state=PDA.State.q0; input=[]; stack=['Z';];})


//    [<Test;
//        Category("Normal Case");
//        Description("Should return a new Configuration with only the front element")>]
//    member x.TestGetConfigs() =
//        let cfgs = seq [ {state=PDA.State.q0; input=['b';]; stack="Z";}; {state=PDA.State.q0; input=['b';]; stack="Z";}; {state=PDA.State.q0; input=['b';]; stack="Z";} ]
//        let mvs :list<Move> = [{state=PDA.State.q0; stack="Z";}; {state=PDA.State.q0; stack="Z";}; {state=PDA.State.q0; stack="Z";};]
//        let newcfgs = getConfigs({state=PDA.State.q1; input=['a';'b';]; stack="Z";}, mvs)
//        let bools :list<bool> = zipWith (fun(y, z) -> y = z), (Seq.to_list cfgs), (Seq.to_list newcfgs)
//        Seq.e
//        Assert.IsTrue(b)

        
    [<Test;
        Category("Normal Case");
        Description("build up a dictionary")>]
    member x.TestBuildDict() = 
        let d = new Dictionary<Config, Moves>()
        d.[{state=PDA.State.q0; input=['a';]; stack=['Z';];}] <- [{state=PDA.State.q0; stack=['a';'Z';];}; {state=PDA.State.q1; stack=['Z';];};]
        d.[{state=PDA.State.q0; input=['b';]; stack=['Z';];}] <- [{state=PDA.State.q0; stack=['b';'Z';];}; {state=PDA.State.q1; stack=['Z';];};]
        d.[{state=PDA.State.q0; input=['a';]; stack=['a';];}] <- [{state=PDA.State.q0; stack=['a';'a';];}; {state=PDA.State.q1; stack=['a';];};]
        d.[{state=PDA.State.q0; input=['b';]; stack=['a';];}] <- [{state=PDA.State.q0; stack=['b';'a';];}; {state=PDA.State.q1; stack=['a';];};]
        d.[{state=PDA.State.q0; input=['a';]; stack=['b';];}] <- [{state=PDA.State.q0; stack=['a';'b';];}; {state=PDA.State.q1; stack=['b';];};]
        d.[{state=PDA.State.q0; input=['b';]; stack=['b';];}] <- [{state=PDA.State.q0; stack=['b';'b';];}; {state=PDA.State.q1; stack=['b';];};]
        d.[{state=PDA.State.q0; input=[]; stack=['Z';];}] <- [{state=PDA.State.q1; stack=['Z';];};];
        d.[{state=PDA.State.q0; input=[]; stack=['a';];}] <- [{state=PDA.State.q1; stack=['a';];};];
        d.[{state=PDA.State.q0; input=[]; stack=['b';];}] <- [{state=PDA.State.q1; stack=['b';];};];
        d.[{state=PDA.State.q1; input=['a';]; stack=['a';];}] <- [{state=PDA.State.q1; stack=[];};];
        d.[{state=PDA.State.q1; input=['b';]; stack=['b';];}] <- [{state=PDA.State.q1; stack=[];};];
        d.[{state=PDA.State.q1; input=[]; stack=['Z';];}] <- [{state=PDA.State.q2; stack=['Z';];};];
        
        let cfgs:list<Config * list<Move>> = [    ({state=PDA.State.q0; input=['a';]; stack=['Z';];}, [{state=PDA.State.q0; stack=['a';'Z';];}; {state=PDA.State.q1; stack=['Z';];};]);
                                                  ({state=PDA.State.q0; input=['b';]; stack=['Z';];}, [{state=PDA.State.q0; stack=['b';'Z';];}; {state=PDA.State.q1; stack=['Z';];};]);
                                                  ({state=PDA.State.q0; input=['a';]; stack=['a';];}, [{state=PDA.State.q0; stack=['a';'a';];}; {state=PDA.State.q1; stack=['a';];};]);
                                                  ({state=PDA.State.q0; input=['b';]; stack=['a';];}, [{state=PDA.State.q0; stack=['b';'a';];}; {state=PDA.State.q1; stack=['a';];};]);
                                                  ({state=PDA.State.q0; input=['a';]; stack=['b';];}, [{state=PDA.State.q0; stack=['a';'b';];}; {state=PDA.State.q1; stack=['b';];};]);
                                                  ({state=PDA.State.q0; input=['b';]; stack=['b';];}, [{state=PDA.State.q0; stack=['b';'b';];}; {state=PDA.State.q1; stack=['b';];};]);
                                                  ({state=PDA.State.q0; input=[]; stack=['Z';]}, [{state=PDA.State.q1; stack=['Z';];};]);
                                                  ({state=PDA.State.q0; input=[]; stack=['a';];},  [{state=PDA.State.q1; stack=['a';];};]);
                                                  ({state=PDA.State.q0; input=[]; stack=['b';];}, [{state=PDA.State.q1; stack=['b';];};];);
                                                  ({state=PDA.State.q1; input=['a';]; stack=['a';];}, [{state=PDA.State.q1; stack=[];};]);
                                                  ({state=PDA.State.q1; input=['b';]; stack=['b';];}, [{state=PDA.State.q1; stack=[];};]);
                                                  ({state=PDA.State.q1; input=[]; stack=['Z';];}, [{state=PDA.State.q2; stack=['Z';];};]);
                                                  ];
        
        let dict = buildDict(cfgs, new Dictionary<Config, Moves>())
        Assert.AreEqual(d, dict)
        
       
    [<Test;
        Category("Normal Case");
        Description("Creates a dictionary based off of the input")>]
    member x.TestCreatDict() = 
        let d = new Dictionary<Config, Moves>()
        d.[{state=PDA.State.q0; input=['a';]; stack=['Z';];}] <- [{state=PDA.State.q0; stack=['a';'Z';];}; {state=PDA.State.q1; stack=['Z';];};]
        d.[{state=PDA.State.q0; input=['b';]; stack=['Z';];}] <- [{state=PDA.State.q0; stack=['b';'Z';];}; {state=PDA.State.q1; stack=['Z';];};]
        d.[{state=PDA.State.q0; input=['a';]; stack=['a';];}] <- [{state=PDA.State.q0; stack=['a';'a';];}; {state=PDA.State.q1; stack=['a';];};]
        d.[{state=PDA.State.q0; input=['b';]; stack=['a';];}] <- [{state=PDA.State.q0; stack=['b';'a';];}; {state=PDA.State.q1; stack=['a';];};]
        d.[{state=PDA.State.q0; input=['a';]; stack=['b';];}] <- [{state=PDA.State.q0; stack=['a';'b';];}; {state=PDA.State.q1; stack=['b';];};]
        d.[{state=PDA.State.q0; input=['b';]; stack=['b';];}] <- [{state=PDA.State.q0; stack=['b';'b';];}; {state=PDA.State.q1; stack=['b';];};]
        d.[{state=PDA.State.q0; input=[]; stack=['Z';];}] <- [{state=PDA.State.q1; stack=['Z';];};];
        d.[{state=PDA.State.q0; input=[]; stack=['a';];}] <- [{state=PDA.State.q1; stack=['a';];};];
        d.[{state=PDA.State.q0; input=[]; stack=['b';];}] <- [{state=PDA.State.q1; stack=['b';];};];
        d.[{state=PDA.State.q1; input=['a';]; stack=['a';];}] <- [{state=PDA.State.q1; stack=[];};];
        d.[{state=PDA.State.q1; input=['b';]; stack=['b';];}] <- [{state=PDA.State.q1; stack=[];};];
        d.[{state=PDA.State.q1; input=[]; stack=['Z';];}] <- [{state=PDA.State.q2; stack=['Z';];};];
        
        let cfgs:list<Config * list<Move>> = [    ({state=PDA.State.q0; input=['a';]; stack=['Z';];}, [{state=PDA.State.q0; stack=['a';'Z';];}; {state=PDA.State.q1; stack=['Z';];};]);
                                                  ({state=PDA.State.q0; input=['b';]; stack=['Z';];}, [{state=PDA.State.q0; stack=['b';'Z';];}; {state=PDA.State.q1; stack=['Z';];};]);
                                                  ({state=PDA.State.q0; input=['a';]; stack=['a';];}, [{state=PDA.State.q0; stack=['a';'a';];}; {state=PDA.State.q1; stack=['a';];};]);
                                                  ({state=PDA.State.q0; input=['b';]; stack=['a';];}, [{state=PDA.State.q0; stack=['b';'a';];}; {state=PDA.State.q1; stack=['a';];};]);
                                                  ({state=PDA.State.q0; input=['a';]; stack=['b';];}, [{state=PDA.State.q0; stack=['a';'b';];}; {state=PDA.State.q1; stack=['b';];};]);
                                                  ({state=PDA.State.q0; input=['b';]; stack=['b';];}, [{state=PDA.State.q0; stack=['b';'b';];}; {state=PDA.State.q1; stack=['b';];};]);
                                                  ({state=PDA.State.q0; input=[]; stack=['Z';]}, [{state=PDA.State.q1; stack=['Z';];};]);
                                                  ({state=PDA.State.q0; input=[]; stack=['a';];},  [{state=PDA.State.q1; stack=['a';];};]);
                                                  ({state=PDA.State.q0; input=[]; stack=['b';];}, [{state=PDA.State.q1; stack=['b';];};];);
                                                  ({state=PDA.State.q1; input=['a';]; stack=['a';];}, [{state=PDA.State.q1; stack=[];};]);
                                                  ({state=PDA.State.q1; input=['b';]; stack=['b';];}, [{state=PDA.State.q1; stack=[];};]);
                                                  ({state=PDA.State.q1; input=[]; stack=['Z';];}, [{state=PDA.State.q2; stack=['Z';];};]);
                                                  ];
        
        let dict = createDict(cfgs)
        Assert.AreEqual(d, dict)
        
        
    [<Test;
        Category("Normal Case");
        Description("runs our args")>]
    member x.TestRunArg() = 
        let cfgs:list<Config * list<Move>> = [    ({state=PDA.State.q0; input=['a';]; stack=['Z';];}, [{state=PDA.State.q0; stack=['a';'Z';];}; {state=PDA.State.q1; stack=['Z';];};]);
                                                  ({state=PDA.State.q0; input=['b';]; stack=['Z';];}, [{state=PDA.State.q0; stack=['b';'Z';];}; {state=PDA.State.q1; stack=['Z';];};]);
                                                  ({state=PDA.State.q0; input=['a';]; stack=['a';];}, [{state=PDA.State.q0; stack=['a';'a';];}; {state=PDA.State.q1; stack=['a';];};]);
                                                  ({state=PDA.State.q0; input=['b';]; stack=['a';];}, [{state=PDA.State.q0; stack=['b';'a';];}; {state=PDA.State.q1; stack=['a';];};]);
                                                  ({state=PDA.State.q0; input=['a';]; stack=['b';];}, [{state=PDA.State.q0; stack=['a';'b';];}; {state=PDA.State.q1; stack=['b';];};]);
                                                  ({state=PDA.State.q0; input=['b';]; stack=['b';];}, [{state=PDA.State.q0; stack=['b';'b';];}; {state=PDA.State.q1; stack=['b';];};]);
                                                  ({state=PDA.State.q0; input=[]; stack=['Z';]}, [{state=PDA.State.q1; stack=['Z';];};]);
                                                  ({state=PDA.State.q0; input=[]; stack=['a';];},  [{state=PDA.State.q1; stack=['a';];};]);
                                                  ({state=PDA.State.q0; input=[]; stack=['b';];}, [{state=PDA.State.q1; stack=['b';];};];);
                                                  ({state=PDA.State.q1; input=['a';]; stack=['a';];}, [{state=PDA.State.q1; stack=[];};]);
                                                  ({state=PDA.State.q1; input=['b';]; stack=['b';];}, [{state=PDA.State.q1; stack=[];};]);
                                                  ({state=PDA.State.q1; input=[]; stack=['Z';];}, [{state=PDA.State.q2; stack=['Z';];};]);
                                                  ];
        
        let dict = createDict(cfgs)
        //this is such a bad hack to get this test working. need to figure out how to better do this... We should'nt
        //have to know what the closure is, to test this...
        let getMoves c =
                let ok, moves = normalizeConfig c |> dict.TryGetValue
                let ok2, lambdaMoves = normalizeConfig {state=c.state; input=[]; stack=c.stack} |> dict.TryGetValue
                if ok && ok2 then 
                    moves, lambdaMoves
                else if ok then 
                    moves, []
                else if ok2 then 
                    [], lambdaMoves
                else [], []
        Assert.IsTrue(runArg(PDA.State.q2, getMoves, {state=PDA.State.q0; input=['a';]; stack=['Z';];}, 1))
        Assert.IsFalse(runArg(PDA.State.q2, getMoves, {state=PDA.State.q0; input=['a';'b';]; stack=['Z';];}, 1))
        
        
    [<Test;
        Category("Normal Case");
        Description("runs a string through our configuration")>]
    member x.TestParseString() = 
        let q0, q1, q2 = PDA.State.q0, PDA.State.q1, PDA.State.q2
        let cfgs:list<Config * list<Move>> = [    ({state=q0; input=['a';]; stack=['Z';];}, [{state=q0; stack=['a';'Z';];}; {state=q1; stack=['Z';];};]);
                                                  ({state=q0; input=['b';]; stack=['Z';];}, [{state=q0; stack=['b';'Z';];}; {state=q1; stack=['Z';];};]);
                                                  ({state=q0; input=['a';]; stack=['a';];}, [{state=q0; stack=['a';'a';];}; {state=q1; stack=['a';];};]);
                                                  ({state=q0; input=['b';]; stack=['a';];}, [{state=q0; stack=['b';'a';];}; {state=q1; stack=['a';];};]);
                                                  ({state=q0; input=['a';]; stack=['b';];}, [{state=q0; stack=['a';'b';];}; {state=q1; stack=['b';];};]);
                                                  ({state=q0; input=['b';]; stack=['b';];}, [{state=q0; stack=['b';'b';];}; {state=q1; stack=['b';];};]);
                                                  ({state=q0; input=[]; stack=['Z';]}, [{state=q1; stack=['Z';];};]);
                                                  ({state=q0; input=[]; stack=['a';];},  [{state=q1; stack=['a';];};]);
                                                  ({state=q0; input=[]; stack=['b';];}, [{state=q1; stack=['b';];};];);
                                                  ({state=q1; input=['a';]; stack=['a';];}, [{state=q1; stack=[];};]);
                                                  ({state=q1; input=['b';]; stack=['b';];}, [{state=q1; stack=[];};]);
                                                  ({state=q1; input=[]; stack=['Z';];}, [{state=q2; stack=['Z';];};]);
                                                  ];
        Assert.IsTrue(parseString(PDA.State.q2, cfgs, {state=q0; input=['a';'b';'b';'a';]; stack=['Z';];}))